home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.1 (Developer) [x86]
/
NeXT Step 3.1 Intel dev.cdr.dmg
/
NextDeveloper
/
Examples
/
AppKit
/
Graph
/
expr.ym
< prev
next >
Wrap
Text File
|
1992-04-19
|
8KB
|
264 lines
%{
/*
expr.ym
This file defines the grammar for parsing expressions. To fully understand
this code you will need to understand yacc. The basic idea is that
the parse tree is built from the bottom up as larger and larger
sub-expressions are recognized by the grammar. The nodes of the tree
are created by the alloc* functions at the end of the file. These
functions are called by the rules of the grammar as various types of
sub-expressions are recognized. The one entry point to this file,
_EXPParseExpression() encapsulates all the lex and yacc glue necessary
to parse an expression.
*/
#import <stdlib.h>
#import <string.h>
#import <stdio.h>
#import "exprDefs.h"
static Term *allocBinaryOpTerm(char op, Term *t1, Term *t2);
static Term *allocVarTerm(char *name);
static Term *allocConstantTerm(float value, BOOL isInt);
static Term *allocFuncTerm(char *name, TermList *args);
static void addAllocedTerm(Term *t);
static void yyerror(char *s);
extern yylex(), yyparse();
/* globals used to build up results of the parse */
static NXHashTable *ValidFuncTerms; /* list of funcs we allow */
static NXHashTable *VarTerms; /* vars found in expression */
static Term *CompleteExpr; /* top of parse tree */
static BOOL ParseError; /* was there a parse error? */
static Term **TermsAlloced; /* terms alloced during parse */
static int NumTermsAlloced; /* #terms alloced during parse */
static NXZone *ParseZone; /* zone to allocate parse results in */
/* initial size of TermsAlloced ptr array */
#define STACK_TERMS 200
%}
%token IDENTIFIER NUMBER INTEGER
%union {
Term *node;
TermList *list;
float real;
int integer;
char *string;
char character;
}
%token <real> NUMBER
%token <integer> INTEGER
%token <string> IDENTIFIER
%token <character> BADCHAR
%type <node> complete_expr expr function
%type <list> arglist
%start complete_expr
%left '+' '-'
%left '*' '/' '%'
%left UMINUS
%right '^'
%%
complete_expr: expr
{ CompleteExpr = $$; }
;
expr : '(' expr ')'
{ $$ = $2; }
| expr '+' expr
{ $$ = allocBinaryOpTerm('+', $1, $3); }
| expr '-' expr
{ $$ = allocBinaryOpTerm('-', $1, $3); }
| expr '*' expr
{ $$ = allocBinaryOpTerm('*', $1, $3); }
| expr '/' expr
{ $$ = allocBinaryOpTerm('/', $1, $3); }
| expr '%' expr
{ $$ = allocBinaryOpTerm('%', $1, $3); }
| expr '^' expr
{ $$ = allocBinaryOpTerm('^', $1, $3); }
| '-' expr %prec UMINUS
{ $$ = _EXPAllocTerm(ParseZone, binOpTerm, 1, $2);
$$->data.binOp.op = '-';
}
| function
| IDENTIFIER
{ $$ = allocVarTerm($1); }
| NUMBER
{ $$ = allocConstantTerm($1, NO); }
| INTEGER
{ $$ = allocConstantTerm($1, YES); }
;
function : IDENTIFIER '(' ')'
{ $$ = allocFuncTerm($1, NULL); }
| IDENTIFIER '(' arglist ')'
{ $$ = allocFuncTerm($1, $3); }
;
arglist : expr
{ $$ = NXZoneMalloc(NXDefaultMallocZone(), sizeof(TermList));
$$->terms[0] = $1;
$$->num = 1;
}
| arglist ',' expr
{ $$ = NXZoneRealloc(NXDefaultMallocZone(), $1,
sizeof(TermList) + $1->num*sizeof(Term *));
$$->terms[$$->num] = $3;
$$->num++;
}
;
%%
/*
* The main entry point into this file. This routine sets up some globals
* and calls yyparse(), a routine generated by yacc, to do the actual parse.
* If the parse succeeds, the results are found in the globals, and are
* returned to the caller.
*
* Note because of the globals this files uses to communicate between this
* routine and the parsing guts, this is not thread safe (the yacc and lex
* internals probably aren't thread safe either). One easy solution to this
* would be to put a mutex lock around the whole parse.
*
* If there is a parse error, since the tree is built bottom up it can be
* difficult to free the data structures allocated before the error is
* detected. To solve this problem we keep a global buffer of pointers to
* the parse tree nodes as they are allocated. In the event of an error
* we can then easily free them all regardless of the state of the parse tree.
* Its also very important to empty the varTerms hash table in this case, so
* it is not returned full of freed nodes.
*/
BOOL _EXPParseExpression(const char *text, NXHashTable *validTerms, Term **parseTree, NXHashTable *varTerms, NXZone *zone) {
int parseRet;
Term *termsAllocedStackSpace[STACK_TERMS];
int i;
CompleteExpr = NULL; /* set globals to prepare for parsing */
VarTerms = varTerms;
ParseError = NO;
ValidFuncTerms = validTerms;
TermsAlloced = termsAllocedStackSpace;
NumTermsAlloced = 0;
ParseZone = zone;
_EXPPrepareToScan(text); /* sets up lex to scan the string */
parseRet = yyparse();
if (parseRet == 1 || ParseError) {
for (i = 0; i < NumTermsAlloced; i++)
_EXPFreeTerm(NULL, TermsAlloced[i]);
*parseTree = NULL;
NXEmptyHashTable(varTerms);
} else
*parseTree = CompleteExpr;
if (NumTermsAlloced > STACK_TERMS)
NXZoneFree(NXDefaultMallocZone(), TermsAlloced);
return !(parseRet == 1 || ParseError);
}
/* allocates a new binary operator term */
static Term *allocBinaryOpTerm(char op, Term *t1, Term *t2) {
Term *newTerm;
newTerm = _EXPAllocTerm(ParseZone, binOpTerm, 2, t1, t2);
newTerm->data.binOp.op = op;
addAllocedTerm(newTerm);
return newTerm;
}
/*
* Allocates a new variable term. We first check to see if the variable has
* already been seen in this parse, and if so return the existing variable
* term. Otherwise we go ahead and allocate a new one.
*/
static Term *allocVarTerm(char *name) {
Term *newTerm;
Term key;
key.tag = varTerm;
key.data.var.name = name;
newTerm = NXHashGet(VarTerms, &key);
if (!newTerm) {
newTerm = _EXPAllocTerm(ParseZone, varTerm, 0);
newTerm->data.var.name = NXCopyStringBufferFromZone(name, ParseZone);
NXHashInsert(VarTerms, newTerm);
addAllocedTerm(newTerm);
}
free(name);
return newTerm;
}
/* allocates a new constant term */
static Term *allocConstantTerm(float value, BOOL isInt) {
Term *newTerm;
newTerm = _EXPAllocTerm(ParseZone, constantTerm, 0);
newTerm->data.constant.val = value;
newTerm->data.constant.isInt = isInt;
addAllocedTerm(newTerm);
return newTerm;
}
/*
* Allocates a new function term. It looks up the function by name to see
* if it is one we know how to parse. If so, it makes sure the number of
* arguments being passed is correct for the function. If the function is
* unknown or the number of arguments is wrong, we record the fact that
* we've had a parse error in a global.
*/
static Term *allocFuncTerm(char *name, TermList *args) {
Term *newTerm;
Function *func;
Function key;
TermList noArgs;
if (!args) {
args = &noArgs;
args->num = 0;
}
key.name = (char *)name;
func = NXHashGet(ValidFuncTerms, &key);
newTerm = _EXPAllocTerm(ParseZone, funcTerm, args->num);
bcopy(args->terms, newTerm->subterms, args->num * sizeof(Term *));
newTerm->data.func.type = func;
if (!func || args->num < func->minArgs ||
(args->num > func->maxArgs && func->maxArgs != -1))
ParseError = YES;
NXZoneFree(NXDefaultMallocZone(), args);
free(name);
addAllocedTerm(newTerm);
return newTerm;
}
/*
* Adds a term to the list of terms that we have allocated during this parse.
* This routine allocates more space for Term pointers if necessary. It knows
* not to free the initial buffer of these pointers, since that buffer is
* allocated on the stack by _EXPParseExpression().
*/
static void addAllocedTerm(Term *t) {
Term **newSpace;
if (!(NumTermsAlloced % STACK_TERMS) && NumTermsAlloced > 0) {
newSpace = NXZoneMalloc(NXDefaultMallocZone(),
(NumTermsAlloced + STACK_TERMS) * sizeof(Term *));
bcopy(TermsAlloced, newSpace, NumTermsAlloced * sizeof(Term *));
if (NumTermsAlloced > STACK_TERMS)
NXZoneFree(NXDefaultMallocZone(), TermsAlloced);
TermsAlloced = newSpace;
}
TermsAlloced[NumTermsAlloced++] = t;
}
/* a piece of yacc glue called when there is a parse error */
static void yyerror(char *s) {}